﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using TSTree.fuctions;

namespace TSTree
{
   public class TSData : IComparable, IStreamable
    {
        public const int SIZEOFDIMENSION = 4;
        public const int SIZEOFFLOAT = 4;
        public const int SIZEOFID = 4;

        public float[] data = null;   // Vector
        public float[] point = null;

        public float distance = 0;      // anything

        public int dimension = 0;      // array size of data[]
        public int id = 0;

        //--------------------------------------------------------------------------
        /**
        * Default Constructor
        */
        public TSData()
        {
            this.setDimension(Constants.RTDataNodeDimension);
           
        }
        /**
        * Constructor accept an int _dimension
        */
        public TSData(int dim)
        {
            this.setDimension(dim);
           
        }


        public TSData(int dim, int id)
        {
            this.setDimension(dim);
            this.id = id;
        }

        public TSData(int dim, int id, bool sax)
        {
            this.setDimension(dim);
            this.id = id;
     
        }

        /**
        * Constructor accept a byte array representing dimension
        */
        public TSData(byte[] bytes)
        {
            try
            {
                this.readDataHeader(bytes);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
        //--------------------------------------------------------------------------
        /**
        * Constructor accept a byte array representing dimension
        */
        private void setDimension(int dim)
        {
            if (dim <= 0) dimension = 1;
            this.dimension = dim;
            //this.data = new float[_dimension];
            this.data = new float[dimension * 2];
            this.point = new float[dimension];

        }
        //--------------------------------------------------------------------------
        /**
        * returns MBR (Minimum Bounding Rect) of the object
        */
        public float[] getMBR()
        {
            float[] f = new float[2 * this.dimension];
            for (int i = 0; i < 2 * dimension; i++)
            {
                f[i] = data[i];
            }

            return f;
        }

        public float[] getPoint()
        {
            float[] f = new float[this.dimension];
            for (int i = 0; i < dimension; i++)
            {
                f[i] = point[i];
            }

            return f;
        }

        public float minDist(float[] point)
        {
            float f = 0;
            f = Utilities.DTWdistance(dimension, 0, point, this.data);
            return f;
        }

        //--------------------------------------------------------------------------
        /**
        * returns the area of the MBR, for vectors always 0.0
        */
        public float getArea()
        {
            return 0;
        }

        /**
        * Implement Streamable
        * fill buffer with my data header
        */
        //--------------------------------------------------------------------------
        /**
        * Implement Streamable
        * return data header size
        */
        public int getDataHeaderSize()
        {
            //return SIZEOFDIMENSION;
            //New
            return SIZEOFID + SIZEOFDIMENSION;
        }

        public void readDataHeader(byte[] buffer)
        {
            MemoryStream byteIn = new MemoryStream(buffer);
            BinaryReader inReader = new BinaryReader(byteIn);
            int dim = inReader.ReadInt32();
            this.setDimension(dim);

            ////////////////////
            //New
            id = inReader.ReadInt32();
            //////////////////

            inReader.Close();
            byteIn.Close();
        }

        public void writeDataHeader(byte[] buffer)
        {
            MemoryStream byteOut = new MemoryStream(buffer.Length);
            BinaryWriter outWriter = new BinaryWriter(byteOut);
            outWriter.Write(this.dimension);

            //////////////////
            //New
            outWriter.Write(id);
            //////////////////

            byte[] bytes = byteOut.ToArray();
            for (int i = 0; i < buffer.Length; ++i)
                buffer[i] = bytes[i];
            outWriter.Close();
            byteOut.Close();
        }
        //----------------------------------------------------------------------------
        /**
        * Implement Streamable
        * returns amount of needed space in bytes
        */
        public int getSize()
        {
            //return this.dimension * 2 * SIZEOFFLOAT + SIZEOFDIMENSION;
            //New
            return this.dimension * 2 * SIZEOFFLOAT + SIZEOFDIMENSION + getDataHeaderSize();
        }
        /**
          * Implement Streamable
          * reads data from buffer
          */
        public void readFromBuffer(byte[] buffer)
        {
            MemoryStream byteIn = new MemoryStream(buffer);
            BinaryReader inReader = new BinaryReader(byteIn);

            readFromBuffer(inReader);

            inReader.Close();
            byteIn.Close();
        }
        /**
         * Implement Streamable
         * fill buffer with my content
         */
        public void writeToBuffer(byte[] buffer)
        {
            MemoryStream byteOut = new MemoryStream(buffer.Length);
            BinaryWriter outWriter = new BinaryWriter(byteOut);

            writeToBuffer(outWriter);

            byte[] bytes = byteOut.ToArray();
            for (int i = 0; i < buffer.Length; ++i)
                buffer[i] = bytes[i];

            outWriter.Close();
            byteOut.Close();
        }

        /**
        * The following i/o functions are used by RTDataNode
        * to read/write data immediately to a stream
        */
        public void readFromBuffer(BinaryReader inReader)
        {
            //////////////////////////////////////
            //New
            int dim = inReader.ReadInt32();
            this.setDimension(dim);
            id = inReader.ReadInt32();
            //////////////////////////////////////

            //for (int i = 0; i < this.dimension; ++i)
            for (int i = 0; i < this.dimension * 2; ++i)
                this.data[i] = inReader.ReadSingle();
            this.distance = inReader.ReadSingle();
        }

        public void writeToBuffer(BinaryWriter outWriter)
        {
            /////////////////////////////////////////
            //New
            outWriter.Write(this.dimension);
            outWriter.Write(id);
            ////////////////////////////////////////

            //for (int i = 0; i < this.dimension; ++i)
            for (int i = 0; i < this.dimension * 2; ++i)
                outWriter.Write(this.data[i]);
            outWriter.Write(this.distance);
        }
        //--------------------------------------------------------------------------
        /**
        * print out all data
        */
        public void print()
        {
            Console.Write("[id: <" + this.id + "> ");
            if (this.dimension > 0)
                Console.Write(this.data[0]);
            for (int i = 1; i < this.dimension * 2; i++)
                Console.Write(" " + this.data[i]);
            Console.WriteLine("]");
        }
        /**
        * Implements Object.toString()
        */
        public override String ToString()
        {
            String answer = this.GetType().Name;
            answer = answer + "(distance=" + this.distance + "," + this.dimension + ":[";
            if (this.dimension > 0)
                answer = answer + this.data[0];
            // for (int i = 1; i < this.dimension; ++i)
            for (int i = 1; i < this.dimension * 2; ++i)
                answer = answer + " " + this.data[i];
            answer = answer + "])";
            return answer;
        }
        //--------------------------------------------------------------------------
        /**
        * Implements the Comparable interface
        */
        public int CompareTo(Object obj)
        {
            if (!(obj is TSData))
                return 1; //??? return arbitary value saying that not equal
            TSData other = (TSData)obj;
            if (this.distance > other.distance) return 1;
            if (this.distance < other.distance) return -1;
            return 0;
        }
        /**
        * Override Object.equals(...)
        */
        public bool equals(Object obj)
        {
            if (!(obj is TSData))
                return false;
            TSData other = (TSData)obj;
            if (this.dimension == other.dimension
            && this.distance == other.distance)
            {
                for (int i = 0; i < this.data.Length; ++i)
                    if (this.data[i] != other.data[i])
                        return false;
                return true;
            }
            return false;
        }

        //--------------------------------------------------------------------------
        /**
        * set this to other.clone()
        */
        public TSData assign(TSData other)
        {
            this.setDimension(other.dimension);
            this.distance = other.distance;
            //for (int i = 0; i < this.dimension; ++i)
            for (int i = 0; i < this.dimension * 2; ++i)
                this.data[i] = other.data[i];
            this.id = other.id;
            return this;
        }
        /**
        * Override Object.clone()
        */
        public Object clone()
        {
            TSData d = new TSData(this.dimension);
            d.distance = this.distance;
            //for (int i = 0; i < this.dimension; ++i)
            for (int i = 0; i < this.dimension * 2; ++i)
                d.data[i] = this.data[i];
            for (int i = 0; i < this.dimension; ++i)
                d.point[i] = this.point[i];

            d.id = this.id;
            return (Object)d;
        }


    }
}
